package net.homelinux.chaoswg.io.renga.core.gui;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import net.homelinux.chaoswg.io.renga.core.gui.operations.CompositeOperation;
import net.homelinux.chaoswg.io.renga.core.gui.operations.Control;
import net.homelinux.chaoswg.io.renga.core.gui.operations.FunctionInputOperation;
import net.homelinux.chaoswg.io.renga.core.gui.operations.FunctionOutputOperation;
import net.homelinux.chaoswg.io.renga.core.gui.operations.Operation;
import net.homelinux.chaoswg.io.renga.core.gui.operations.helper.CantGetResultException;
import net.homelinux.chaoswg.io.renga.core.gui.operations.helper.ConversionException;
import net.homelinux.chaoswg.io.renga.core.registries.GlobalPropertyRegistry;
import net.homelinux.chaoswg.io.renga.core.registries.OperationRegistry;
import net.homelinux.chaoswg.io.renga.krypto.gui.operations.LoopOperation;

public class TopPanel extends JPanel implements Observer, PropertyChangeListener {
    private final GUI parent;
    
    private final GlobalPropertyRegistry gpr = GlobalPropertyRegistry.getGlobalPropertyRegistry();

    //We are observing a composite operation
    private final CompositeOperation compositeOperation;
    //and it's controll
    private final Control control;

    //We distingush between loop and nonloop
    private final boolean isLoop;

    //We need buttons to start the simulation
    private final JButton start = new JButton("Start");
    //Add a new operation
    private final JButton add = new JButton ("Add");
    //Remove the selected operatio
    private final JButton remove = new JButton ("Remove selected");
    //Add a new input 
    private final JButton addInput = new JButton ("Add Input");
    //or a new output
    private final JButton addOutput = new JButton ("Add Output");
    //and something to select the operation to add from
    private final JComboBox operations = new JComboBox();
    
    private final JSlider animationSpeedSlider = new JSlider (5, 20);
    private final JCheckBox animate = new JCheckBox();
    
    public TopPanel (final GUI parent, final CompositeOperation compositeOperation) {
        //We safe the parent 
        this.parent = parent;
        //The composite we are about to watch
        this.compositeOperation = compositeOperation;
        //The control of the composite to watch
        this.control = compositeOperation.getControl();
        //And check if it is a loop
        this.isLoop = (compositeOperation instanceof LoopOperation);

        //then we add ourselfes as observer for the control and the composite operation
        compositeOperation.addObserver(this);
        compositeOperation.getControl().addObserver(this);

        //We search which operations could be added
        this.insertOperations();

        //Then we add the controloperations
        this.addContent();
        //And listeners for the buttons
        this.addListeners();
        
        gpr.addPropertyChangeListener(this);
        animationSpeedSlider.setValue(((Integer)gpr.getProperty("animationSpeed")));
        animate.setSelected(((Boolean)gpr.getProperty("animationStatus")) );
    }

    private void addContent() {
        final JPanel top = new JPanel (new BorderLayout ());
        final JPanel bottom = new JPanel (new BorderLayout ());
        //We set ourselfes to borderlayout
        this.setLayout(new BorderLayout ());
        //And add panels for the buttongroups
        final JPanel left = new JPanel (new FlowLayout (FlowLayout.LEADING)); 
        final JPanel center = new JPanel (); 
        final JPanel right = new JPanel (new FlowLayout (FlowLayout.TRAILING));
        
        final JPanel tleft = new JPanel (new FlowLayout(FlowLayout.LEADING));
        final JPanel tright = new JPanel (new FlowLayout (FlowLayout.TRAILING));

        //And then add the buttons
        left.add(start);

        center.add (addInput);
        center.add (addOutput);
        //and the dropbox for operationselection
        right.add (operations);
        right.add (add);
        right.add (remove);
        
        tleft.add(new JLabel ("Animate: "));
        tleft.add(animate);
        tright.add(new JLabel ("Animation Speed: "));
        tright.add(animationSpeedSlider);
        tright.add(new JLabel("(Further to the right is slower)"));

        //if this is a loop we can't add in or outputs
        if (isLoop) {
            addInput.setEnabled(false);
            addOutput.setEnabled(false);
        }

        //And we add the panels        
        bottom.add (left, BorderLayout.WEST);
        bottom.add (center, BorderLayout.CENTER);
        bottom.add (right, BorderLayout.EAST);
        
        top.add (tleft,BorderLayout.WEST);
        top.add (tright, BorderLayout.EAST);
        
        this.add(top, BorderLayout.NORTH);
        this.add(bottom, BorderLayout.SOUTH);
    }

    //We just check the central operationregistry for all operations 
    //And add them as option
    private void insertOperations () {
        for (final String s : OperationRegistry.getOperationRegistry().getOperationNames()) {
            operations.addItem(s);
        }
    }

    //For the buttons we need listeners
    private void addListeners () {
        //Startbutton just tells the controll to start
        start.addActionListener(new ActionListener () {
            public void actionPerformed (ActionEvent ae) {
                String message = "Not all Inputconnectors are connected or there is no initial value set. This may very well be your intend so you have to decied if we go on.\n " +
                "Should one of the Inputconnectors used during the computation not be connected, the String-Value \"0\" will be used.\n " +
                "This may work or crash misserably depending on your setup. So it's your decision.";

                if (control.fullyConnected() || 
                        (  (!control.fullyConnected()) && 
                                (JOptionPane.showConfirmDialog(parent, message, "Your decicion", 
                                        JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION))) {
                    try {
                        control.doit();
                    } catch (CantGetResultException cgrexcp) {
                        JOptionPane.showMessageDialog(parent, cgrexcp.getMessage(), "Can't process your request", JOptionPane.ERROR_MESSAGE);
                    } catch (ConversionException convexcp) {
                        JOptionPane.showMessageDialog(parent, convexcp.getMessage(), "Can't process your request", JOptionPane.ERROR_MESSAGE);
                    } catch (Exception excp) {
                        JOptionPane.showMessageDialog(parent, "Sorry your request couldn't be processed. And I don't know why", "Strange error", JOptionPane.ERROR);
                    }
                }
            }
        });
        //The addbutton
        add.addActionListener(new ActionListener () {
            public void actionPerformed (final ActionEvent ae ) {
                //Tells the controll to add the operation 
                control.addOperation(OperationRegistry.getOperationRegistry().getOperation(((String)operations.getSelectedItem())), false);
            }
        });

        //Remove calls the remove selectedmethod in the control to remove the selected item
        remove.addActionListener(new ActionListener () {
            public void actionPerformed (final ActionEvent ae) {
                control.removeSelected();
            }
        });

        //Add input does what it says 
        addInput.addActionListener(new ActionListener () {
            public void actionPerformed (final ActionEvent ae) {
                control.addOperation(new FunctionInputOperation (compositeOperation), false);
            }
        });

        //As well as add output
        addOutput.addActionListener(new ActionListener () {
            public void actionPerformed (final ActionEvent ae) {
                control.addOperation(new FunctionOutputOperation (compositeOperation), false);
            }
        });
        
        animationSpeedSlider.addChangeListener(new ChangeListener () {
            public void stateChanged (final ChangeEvent ce) {
                gpr.setProperty("animationSpeed", animationSpeedSlider.getValue());
            }
        });

        animate.addChangeListener(new ChangeListener () {
            public void stateChanged (final ChangeEvent ce) {
                gpr.setProperty("animationStatus",  animate.isSelected());
            }
        });

    }

    //we check for updates (selection of an item)
    public void update (final Observable observable, final Object arg) {
        //if this operation is a loop 
        if (isLoop) {
            //We must check if the selected operation
            final Operation selected = control.getSelected();
            //is an instance of an input or output because these can't be removed in loops (for safety reasons)
            if(  (selected instanceof FunctionInputOperation) || (selected instanceof FunctionOutputOperation)) {
                remove.setEnabled(false);
            } else  {
                remove.setEnabled(true);
            }
        }
    }

    public void propertyChange(final PropertyChangeEvent arg0) {
        if (arg0.getSource().equals(gpr) && arg0.getPropertyName().equals("animationSpeed")) {
            this.animationSpeedSlider.setValue(((Integer)arg0.getNewValue()));
        } else if (arg0.getSource().equals(gpr) && arg0.getPropertyName().equals("animationStatus")) {
            final boolean animationStatus = ((Boolean)arg0.getNewValue());
            this.animate.setSelected(animationStatus);
            this.animationSpeedSlider.setEnabled(animationStatus);
        }

    }
}
